<script lang="ts" setup>
import {uploadCheck, uploadChunk, uploadHWYun} from '@/api/upload';
import {message} from 'ant-design-vue';

const {index, title, chapterIndex, hwyunId} = defineProps<{
    index: number
    title: string
    chapterIndex: number
    notAllowOperation?: boolean
    hwyunId?: string
}>()

// 修改父组件数据
const emit = defineEmits<{
    (e: 'del-episode', chapterIndex: number, index: number): void
    (e: 'edit-episode', chapterIndex: number, index: number, title: string): void
}>()

// 是否编辑控制变量
let isEditState = $ref(false)

// 集标题
let titleContent = $ref(title)

// 集标题编辑或保存
function editEpisode() {
    if (!isEditState) {
        emit('edit-episode', chapterIndex, index, titleContent)
    }
    isEditState = !isEditState
}

// 处理上传视频的逻辑
const uploadVideo = $ref<HTMLElement>()

// 初始化文件信息
let fileReactive = $ref({
    file: undefined as File | undefined,
    hash: '',
    type: '',
    uploadedBytes: 0,
    offset: 0,
    size: 0,
    isLoading: false,
    title: ''
})

// 上传具体视频文件
async function handleUpload() {
    fileReactive.isLoading = true
    if (!fileReactive.file) {
        alert('请选择文件')
        return
    }
    // 校验视频是否上传、没上传或者没完成则继续
    const {data, code} = await uploadCheck({
        hash: fileReactive.hash,
        type: fileReactive.type
    })
    if (code !== 0) {
        fileReactive.isLoading = false
        message.error('上传失败，请重试')
        resetFileReactive()
        return
    }
    // 上传的视频字节数
    fileReactive.uploadedBytes = data.uploadedBytes

    // 定义键值对的表单、值必须为字符串
    const formData = new FormData()
    formData.append('hash', fileReactive.hash)
    formData.append('title', fileReactive.title)
    formData.append('size', String(fileReactive.size))
    formData.append('type', fileReactive.type.split('/').pop()!)
    console.log(fileReactive.uploadedBytes < fileReactive.size);

    // 当已经上传的文件字节数小于该文件的总字节数，则继续上传
    while (fileReactive.uploadedBytes < fileReactive.size) {
        // 切分的起点
        const startByte = fileReactive.uploadedBytes
        // 切分结束点，每次上传视频的固定为10兆
        const endByte = Math.min(startByte + 1024 * 1024 * 10, fileReactive.size)
        // 切分文件上传
        const chunk = fileReactive.file.slice(startByte, endByte)
        // 开始的位置
        formData.append('offset', String(startByte))
        // 把上一次的删除，插入新chunk
        formData.delete('chunk')
        formData.append('chunk', chunk)
        // 上传视频文件
        const {data: uploadData, code: uploadCode} = await uploadChunk(formData)
        if (uploadCode !== 0) {
            fileReactive.isLoading = false
            message.error('上传失败，请重试')
            resetFileReactive()
            return
        }
        // 更新上传的字节数
        fileReactive.uploadedBytes = uploadData.uploadedBytes;
    }
    // 当该视频文件全部上传完成，上传华为云
    await new Promise((resolve) => {
        // 文件IO操作需要时间，延时1秒操作
        setTimeout(async () => {
            const {code, msg} = await uploadHWYun({
                title: fileReactive.title,
                episodeId: index,
                type: fileReactive.type
            })
            if (code === 0) {
                message.success(msg || '上传成功')
            }
            resolve('')
        }, 1000);
    })
}

// 重置文件信息
function resetFileReactive() {
    fileReactive = {
        file: undefined,
        hash: '',
        type: '',
        uploadedBytes: 0,
        offset: 0,
        size: 0,
        isLoading: false,
        title: ''
    }
}

// 视频上传点击
async function handleFileChange(e: Event) {
    const file = (e.target as HTMLInputElement).files![0]
    if (file) {
        fileReactive.file = file
        fileReactive.hash = await getFileHash(file)
        fileReactive.type = file.type
        fileReactive.offset = 0
        fileReactive.uploadedBytes = 0
        fileReactive.size = file.size
        fileReactive.title = file.name

        await handleUpload().finally(() => resetFileReactive())
    }
}

// 拿到视频文件的hash
function getFileHash(file: File): Promise<string> {
    return new Promise((resolve) => {
        // 创建FileReader对象，用于读取文件内容
        const reader = new FileReader()
        // 将文件内容作为ArrayBuffer类型的对象传递给onload事件处理函数
        reader.onload = async function () {
            const arrayBuffer = reader.result as ArrayBuffer;
            // 计算哈希值
            crypto.subtle.digest("SHA-256", arrayBuffer)
                    .then(hash => {
                        const hashArray = Array.from(new Uint8Array(hash));
                        // 将其转换为字符串格式
                        const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
                        resolve(hashHex)
                    })
        }
        // 读取文件内容
        reader.readAsArrayBuffer(file)
    })
}

function handleVideoUpload() {
    uploadVideo?.click()
}
</script>

<template>
    <div border="~" p="x-2 y-1" flex items-center justify-between text-base w-full>
        <div flex="~ v-center" gap-4>
            <div flex="~ v-center" gap="0.8">
                <span flex-shrink-0>第{{ index + 1 }}集</span>
                <span v-show="!isEditState">{{ titleContent }}</span>
                <a-input text="base!" v-model:value="titleContent" v-show="isEditState"/>
            </div>
        </div>
        <div flex="~ v-center" gap-4 v-if="!notAllowOperation">
            <a-button type="dashed" size="small" @click="editEpisode">{{
                    isEditState ? '保存' : '编辑'
                }}
            </a-button>
            <a-button v-show="!isEditState" type="primary" size="small" danger
                      @click="$emit('del-episode', chapterIndex, index)">删除
            </a-button>
        </div>

        <div v-else>
            <input type="file" hidden ref="uploadVideo" @change="handleFileChange"/>
            <a-button type="primary" :loading="fileReactive.isLoading" @click="handleVideoUpload()">{{
                    hwyunId ? '修改视频' : '上传视频'
                }}
            </a-button>
        </div>
    </div>
</template>
